Продолжается подписка на наши издания! Вы не забыли подписаться?

Создание выделенных хендлеров сервисов ASP.NET

Автор: Петер Вогел (Peter Vogel)
Опубликовано: 02.11.2006

Если упор делается на производительность Web-приложения, а доступ ограничен внутренними клиентами, может очень пригодиться HTTP-хендлер ASP.NET. HTTP-хендлеры ASP.NET не имеют равных по производительности, когда речь идет о доставке клиенту данных через Web.

Кроме того, что он предоставляет высокопроизводительный доступ к ресурсам сервера, хендлер ASP.NET дает дополнительную гибкость. Вы можете вызвать хендлеры из HTML-страниц, серверного или клиентского кода. Вы можете вызывать хендлеры даже из клиента Windows Forms. Что самое приятное – их легко писать.

Хендлер ASP.NET превосходит по производительности все альтернативы в каждом из описанных сценариев (см. Таблицу 1).

Тип Выполненных
запросов (сек.)
Производительность по сравнению с хендлером
Хендлер 243 0 %
Страница с кодом 126 51 %
Web-сервис 36 15 %

Эффективность хендлера во многом обусловлена тем, чего в нем нет. Подумайте об ASP.NET-хендлере как об ASPX-файле, который не генерирует никаких событий, и не содержит HTML. Благодаря отсутствию этих элементов, порождаемая хендлером нагрузка ниже, а производительность – выше, чем у ASP.NET Web Form.


Рисунок 1.

Первый шаг в создании хендлера – создать на ASP.NET -сайте новый файл с расширением ASHX (см. рисунок 1). Этот файл должен содержать директиву обработки WebHandler и, если вы собираетесь поместить код непосредственно в ASHX-файл, атрибуты Language и Class. Этот пример содержит хендлер, написанный на Visual Basic, и определяющий класс с названием EmployeeInfo:

<%@ WebHandler Language="VB" Class="EmployeeInfo" %>

ASP.NET по умолчанию не создает никакого кода для ASHX-файлов. Многие программисты предпочитают размещать свой код в отдельном файле, особенно в Visual Studio 2003, где для ASHX-файлов нет IntelliSense, подсветки синтаксиса и поддержки отладки. Чтобы использовать файл с кодом из ASHX-файла, нужно добавить в директиву WebHandler атрибут CodeBehind, и задать имя внешнего файла в качестве значения этого атрибута. Затем на Web-сайте размещается Class-файл с указанным именем (Visual Studio 2005 помещает этот файл в папку App_Code вашего проекта). Теперь код больше на размещается в ASHX-файле, и атрибут Language можно удалить из директивы WebHandler:

<%@ WebHandler Class="EmployeeInfo" 
  CodeBehind="EmployeeInfo.vb" %>

Код хендлера должен реализовать интерфейс IHttpHandler. При реализации этого интерфейса нужно написать код одного метода (ProcessRequest) и одного свойства (IsReusable).

Улучшение масштабируемости

Масштабируемость приложения можно увеличить, заставив свойство IsReusable возвращать значение True. Это заставит ASP.NET кэшировать хендлер после первого же пользовательского обращения к нему. Если хендлер задействует какой-либо дефицитный ресурс или используется редко, это свойство должно возвращать False.

Можно также указать тип контента, возвращаемого хендлером, используя свойство ContentType объекта Context, который ASP.NET передает методу ProcessRequest. Протестировать хендлер можно просто сделав ASHX-файл стартовой страницей и нажав F5 (если значение свойства ContentType - "text/HTML"). Стартовая точка хендлера после этих изменений будет выглядеть примерно так:

Imports System
Imports System.Web

Public Class EmployeeInfo Implements IHttpHandler
  Public Sub ProcessRequest(ByVal context As HttpContext) _
      Implements IHttpHandler.ProcessRequest
    Context.Response.ContentType = "text/HTML"
  End Sub

  Public ReadOnly Property IsReusable() As Boolean _
      Implements IHttpHandler.IsReusable
    Get
      Return True
    End Get
  End Property
End Class

К этому моменту вы создали скелет хендлера и поместили бизнес-логику в метод ProcessRequest вашего хендлера (при обращении клиента к хендлеру ASP.NET вызывает этот метод автоматически). У вас также есть доступ к ряду существующих ASP.NET-объектов (включая объекты Request, Response и User) через объект Context, переданный методу ProcessRequest.

Простейший из возможных ProcessRequest возвращает строку, используя метод Write объекта Response:

Public Sub ProcessRequest(ByVal context _
As HttpContext) Implements _
IHttpHandler.ProcessRequest

context.Response.ContentType = _
"text/HTML"
context.Response.Write("Hello World")
End Sub

Объект Request можно использовать также для доступа к любой информации, отправляемой клиентом на сервер в качестве части запроса. Следующий код в процедуре ProcessRequest берет значение параметра "name" из строки запроса и встраивает его в строку, отправляемую обратно браузеру:

context.Response.Write("Hello, " & context.Request.QueryString("name"))

Вы можете запросить хендлер, введя его URL в адресную строку любого браузера. Типичное обращение к хендлеру может выглядеть так:

http://MyServer/MyHandlerSite/EmployeeInfo.ashx?name=Peter

Можно обратиться к хендлеру, используя только URL, что позволяет сделать это с любой Web-страницы. То, что доступ к HTTP-хендлерам описывается в виде URL, позволяет ссылаться на них из HTML-атрибутов, предусматривающих ссылку на URL. Так, на HTTP-хендлер можно ссылаться в атрибуте src тега img или iframe:

<form id="form1" runat="server">
  <div>
    <iframe src="EmployeeInfo.ashx?name=Peter" />
  </div>
</form>

Использование методов объекта Response для передачи содержимого файлов и потоков клиенту позволяет создать хендлер, дающий клиенту возможность запросить файл у сервера. Например, можно использовать метод WriteFile для передачи содержимого файла напрямую в поток вывода без буферизации. Следующий код использует значение параметра «name», получаемого с помощью функции QueryString, для задания локального пути на сервере к графическому файлу, отправляемому клиенту:

context.Response.WriteFile(context.Request.QueryString("name") & ".jpg")

Этот хендлер можно вызвать из атрибута src тега image. Следующий файл добавляет на страницу изображение, хранящееся в файле:

<img src="Photo.ashx?EmployeeName=davolio" alt="Some employee's picture" />

Интеграция хендлера

Хендлер можно интегрировать в окружающее его приложение, обращаясь к пользовательским данным в объекте Session из кода хендлера. К сожалению, по умолчанию вы получите сообщение "Object reference not set to an instance of an object" при попытке использовать свойство Session объекта Context. Реализация интерфейса IRequiresSessionState позволяет получить доступ к объекту Session. Этот интерфейс не требует реализации каких-либо методов или свойств – его наличие сигнализирует ASP.NET о необходимости создания экземпляра объекта Session.

Public Class EmployeeInfo
Implements IHttpHandler
Implements IRequiresSessionState

Этот, более сложный, пример считывает данные из БД Northwind для создания HTML-таблицы с информацией о сотрудниках. Заметьте, что вы не вытягиваете информацию из querystring. Вместо этого для определения того, информация о каких сотрудниках должна быть выведена, используется значение из объекта Session. Для отображения фотографии сотрудника, на которое указывает поле PhotoPath, код строит тег img (см. листинг 1 и рисунок 2).

Листинг 1

Dim sbOutPut As New StringBuilder
Dim con As New SqlConnection( _
ConfigurationManager.ConnectionStrings( _
“NwindConnection”).ConnectionString)
Dim cmd As SqlCommand = con.createcommand
cmd.CommandText = _
“Select EmployeeId, LastName, FirstName, “ & _
“ BirthDate, PhotoPath from Employees “ & _
“ Where LastName ='” & _ 
context.Session(“EmployeeName”) & “';”

con.Open()
Dim rdr As SqlDataReader = cmd.ExecuteReader
sbOutput.Append(“<table>”)
If rdr.Read() Then
For ing As Integer = 0 To rdr.FieldCount - 1
sbOutPut.Append(“<tr>”)
sbOutPut.Append(“<td>” & rdr.GetName(ing) _
& “</td>”)
Select Case rdr.GetName(ing) 
Case “PhotoPath” 
sbOutPut.Append( _
“<td><img src=” & rdr(ing) & “/></td>”)
Case Else
sbOutPut.Append(“<td>” & rdr(ing) & “</td>”)
End Select
sbOutPut.Append(“</tr>”)
Next
End If
con.Close
sbOutPut.Append(“</table>”)
context.Response.Write(sbOutPut.ToString)


Рисунок 2.

Приведенные до сих пор примеры работают с текстом на базовом уровне. Однако хендлер можно использовать и для передачи клиенту бинарных данных, если считать, что клиент сможет их обработать. В таблице Employee БД Northwind в колонке Photo лежит BLOB с фотографией сотрудника. К счастью, тег <img> в броузере рассчитан на получение строки бинарных данных. Все, что должен сделать хендлер – извлечь из поля бинарные данные и отправить их клиенту.

Бинарное поле читается с помощью метода GetBytes объекта DataReader. Этот метод придется вызвать два раза: сперва с Nothing в качестве третьего параметра для определения числа байт в поле. Затем, используя это значение, задается размер буфера. После этого опять вызывается GetBytes, с буфером в качестве третьего параметра, для загрузки данных из поля в буфер.

Начните с выборки записи о сотруднике в объект Session:

Dim Photo() As Byte
Dim lngLength As Long
Dim sbOutPut As New StringBuilder
Dim con As New SqlConnection( _
ConfigurationManager.ConnectionStrings( _
"NwindConnection").ConnectionString)
Dim cmd As SqlCommand = con.createcommand
cmd.CommandText = _
"Select Photo " & _
" Where LastName ='" & _ 
context.Session("EmployeeName") & "';"

Теперь извлеките данные поля Photo:

con.Open()
Dim rdr As SqlDataReader = cmd.ExecuteReader
If rdr.Read() Then
lngLength = rdr.GetBytes(0, 0, Nothing, 0, _
Integer.MaxValue) - 1
ReDim Photo(lngLength)
rdr.GetBytes(0, 0, Photo, 0, lngLength)
con.Close

OutputStream объекта Response предоставляет механизм поточной передачи данных клиенту. Метод Write OutputStream-а используется для передачи клиенту данных из буфера. Заметьте, что первые 78 байт поля Photo нужно пропустить, чтобы получить в теге <img> корректный результат... по причинам, мне неизвестным (в этих полях хранятся OLE-объекты, можно предположить, что эти 78 байт – ни что иное, как заголовок OLE-объекта – прим.ред.):

context.Response.OutputStream.Write(Photo, 78, lngLength - 78)

Используйте этот тег для вызова хендлера и отображения содержимого поля Photo на Web-странице:

<img src="EmployeePhoto.ashx" alt="Empoyee photo" />

За пределами HTML

Хендлеры можно вызывать не только из HTML-тегов. Если нужно обработать в клиентском коде результаты, возвращенные хендлером, можно использовать для его вызова объект XMLHTTP. Следующий код (для IE) использует объект XMLHTTP для вызова хендлера и получения результата:

var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("GET", 
  "http://MyServer/MyHandlerSite/EmployeeInfo.ashx?name=Davolio", false);
xmlhttp.Send(null);
var result = xmlhttp.responseText;

А эта версия кода работает в последних версиях FireFox, Mozilla, Safari и Netscape:

var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET",
  "http://MyServer/MyHandlerSite/EmployeeInfo.ashx?name=Davolio", false);
xmlhttp.send(null);
var result = xmlhttp.responseText;

Хендлер можно вызвать также из серверного кода WebForm, используя объект WebClient. Метод DownloadString объекта WebClient принимает URL хендлера и возвращает результат в виде строки:

Dim wc As New System.Net.WebClient
Me.TextBox1.Text = wc.DownloadString( _
"http://MyServer/MyHandlerSite/" & _
"EmployeeInfo.ashx?name=Peter")

Заметьте, что вам не стоит вызывать хендлер со страницы того же сайта, поскольку это не эффективно. Вместо этого лучше создать объект с необходимой функциональностью и добавить хендлер, выступающий в качестве «фасада» этого объекта. Такой дизайн позволит обращаться к объекту напрямую из серверного кода WebForms, расположенного на том же сайте.

Объект WebClient также полезен. Основанный на WebClient код можно использовать с других сайтов или в клиенте Windows Form для получения данных от ASP.NET-хендлера. Объекты WebClient и XMLHTTP предоставляют любому клиенту высокоэффективный способ получения данных от Web-приложения. Клиентам не нужно создавать прокси с хендлером, в отличие от использования клиентов с Web-сервисами. Клиенты также предоставляют больше способов для интеграции вызовов в код.

Конечно, даром ничего не дается. Если вы отходите от Web-сервисов, вы также отходите и от стандартов, предоставляемых спецификацией Web-сервисов. Вы больше не сможете положиться на WSDL-файл для определения формата сообщений, и вынуждены будете создавать и обрабатывать собственные форматы. Хендлеры эффективны, но их нужно использовать для передачи данных, только если заодно создаются и клиенты, работающие с этими хендлерами.

Теперь вы знаете все, чтобы реализовать синхронный ASP.NET-хендлер. Но вы можете создавать и асинхронные хендлеры, дающие лучшую масштабируемость, чем рассматривавшиеся здесь синхронные. Одно предупреждение: асинхронные хендлеры сложнее писать, чем синхронные. Чтобы воспользоваться асинхронной обработкой, придется запустить новый поток и создать новый класс, возвращающий объект IAsyncResult.

v

v


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.

Copyright © 1994-2016 ООО "К-Пресс"